// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

import { BaseDialog } from "./dialog.slint";
import { MaterialStyleMetrics } from "../styling/material_style_metrics.slint";
import { Icons } from "../icons/icons.slint";
import { MaterialText } from "./material_text.slint";
import { StateLayer } from "./state_layer.slint";
import { ScrollView } from "./scroll_view.slint";
import { IconButton } from "./icon_button.slint";
import { TextField } from "./text_field.slint";
import { MaterialTypography } from "../styling/material_typography.slint";
import { MaterialPalette } from "../styling/material_palette.slint";
import { ExtendedTouchArea } from "./extended_touch_area.slint";

// defines the interface to get the data for the DatePicker from native code.
export global DatePickerAdapter {
    // returns the number of days for the given month in the given year.
    pure callback month_day_count(month: int, year: int) -> int;

    // return the numbers of day to the first monday of the month.
    pure callback month_offset(month: int, year: int) -> int;

    // used to format a date that is defined by day month and year.
    pure callback format_date(format: string, day: int, month: int, year: int) -> string;

    // parses the given date string and returns a list of day, month and year.
    pure callback parse_date(date: string, format: string) -> [int];

    // returns true if the given date is valid.
    pure callback valid_date(date: string, format: string) -> bool;

    // returns the current date as list of day, month and year.
    pure callback date_now() -> [int];
}

export struct Date {
    year: int,
    month: int,
    day: int,
}

component CalendarHeaderDelegate {
    in property <string> text <=> text_label.text;

    min_width: max(MaterialStyleMetrics.size_40, content_layer.min_width);
    min_height: max(MaterialStyleMetrics.size_40, content_layer.min_height);

    content_layer := VerticalLayout {
        text_label := MaterialText {
            vertical_alignment: center;
            horizontal_alignment: center;
            style: MaterialTypography.body_large;
            color: MaterialPalette.on_surface;
        }
    }
}

component CalendarDelegate {
    in property <bool> selected;
    in property <bool> today;
    in property <string> text <=> text_label.text;
    in property <bool> enabled: true;

    callback clicked <=> touch_area.clicked;

    min_width: max(MaterialStyleMetrics.size_40, content_layer.min_width);
    min_height: max(MaterialStyleMetrics.size_40, content_layer.min_height);
    forward_focus: focus_scope;

    accessible_role: button;
    accessible_checkable: true;
    accessible_checked: root.selected;
    accessible_label: root.text;
    accessible_action_default => { touch_area.clicked(); }

    touch_area := TouchArea {
        enabled: root.enabled;
    }

    focus_scope := FocusScope {
        width: 0px;
        enabled: root.enabled;

        key_pressed(event) => {
            if (event.text == " " || event.text == "\n") {
                touch_area.clicked();
                 return accept;
            }

            return reject;
        }
    }

    background_layer := Rectangle {
        border_radius: self.height / 2;
    }

    content_layer := HorizontalLayout {
        text_label := MaterialText {
            vertical_alignment: center;
            horizontal_alignment: center;
            style: MaterialTypography.body_large;
            color: MaterialPalette.on_surface;
        }
    }

    state_layer := StateLayer {
        enabled: root.enabled;
        border_radius: background_layer.border_radius;
        pressed: touch_area.pressed;
        has_hover: touch_area.has_hover;
        has_focus: focus_scope.has_focus;
    }

    states [
        // FIXME: states
        disabled when !root.enabled : {
            root.opacity: 0.38;
        }
        selected when root.selected : {
            background_layer.background: MaterialPalette.primary;
            text_label.color: MaterialPalette.on_primary;
        }
        today when root.today : {
            background_layer.border_width: 1px;
            background_layer.border_color: MaterialPalette.primary;
        }
    ]
}

export component Calendar {
    in property <int> column_count;
    in property <int> row_count;
    in property <length> delegate_size;
    in property <int> start_column;
    in property <[string]> header_model;
    in property <int> month_count;
    in property <Date> today;
    in property <Date> selected_date;
    in property <int> display_month;
    in property <int> display_year;

    callback select_date(date: Date);

    // header
    for day[index] in root.header_model : CalendarHeaderDelegate {
        x: root.delegate_x(index);
        y: root.delegate_y(index);
        text: day;
    }

    // items
    for index in root.month_count : CalendarDelegate {
        property <Date> d: { day: index + 1, month: root.display_month, year: root.display_year };

        x: root.delegate_x(root.index_on_calendar(index));
        y: root.delegate_y(root.index_on_calendar(index));
        width: root.delegate_size;
        height: root.delegate_size;
        text: index + 1;
        selected: root.selected_date == self.d;
        today: root.today == self.d;

        clicked => {
            root.select_date(self.d);
        }
    }

    function index_on_calendar(index: int) -> int {
        // add column count because items starts after header row
        root.column_count + root.start_column + index
    }

    function row_for_index(index: int) -> int {
        floor(index / root.column_count)
    }

    function column_for_index(index: int) -> int {
        mod(index, root.column_count)
    }

    function delegate_x(index: int) -> length {
        root.column_for_index(index) * root.delegate_size + root.column_for_index(index) * 1px /* * root.style.spacing */
    }

    function delegate_y(index: int) -> length {
        root.row_for_index(index) * root.delegate_size + root.row_for_index(index) * 1px /* * root.style.spacing */
    }
}

component YearSelection {
    in property <[int]> model;
    in property <length> spacing;
    in property <int> visible_row_count;
    in property <int> column_count;
    in property <length> delegate_width;
    in property <length> delegate_height;
    in property <int> selected_year;
    in property <int> today_year;

    callback select_year(year: int);

    property <length> row_height: root.height / root.visible_row_count;
    property <int> row_count: root.model.length / root.column_count;
    property <length> viewport_height: root.row_count * root.row_height;
    property <length> start_x: root.width / (root.column_count + 1);
    property <length> start_y: root.height / (root.visible_row_count + 1);

    ScrollView {
        width: 100%;
        height: 100%;
        viewport_width: root.width;
        viewport_height: root.viewport_height;

        for year[index] in root.model: CalendarDelegate {
            x: root.delegate_center_x(index) - self.width / 2;
            y: root.delegate_center_y(index) - self.height / 2;
            width: root.delegate_width;
            height: root.delegate_height;
            text: year;
            selected: year == root.selected_year;
            today: year == root.today_year;

            clicked => {
                root.select_year(year);
            }
        }
    }

    function delegate_center_x(index: int) -> length {
        root.start_x * (root.column_for_index(index) + 1)
    }

    function delegate_center_y(index: int) -> length {
        root.start_y * (root.row_for_index(index) + 1)
    }

    function row_for_index(index: int) -> int {
        floor(index / root.column_count)
    }

    function column_for_index(index: int) -> int {
        mod(index, root.column_count)
    }
}

export component SelectionButton {
    in property <string> text <=> text-label.text;
    in property <image> icon <=> icon-image.source;
    in property <bool> enabled: true;
    in-out property <bool> checked;

    callback clicked();

    min-width: content-layer.min-width;
    min-height: max(40px, content-layer.min-height);
    accessible-label: root.text;
    accessible-role: button;
    accessible-checkable: true;
    accessible-checked: root.checked;
    accessible-action-default => { touch-area.clicked(); }
    forward-focus: touch-area;

    touch-area := ExtendedTouchArea {
        width: 100%;
        height: 100%;
        enabled: root.enabled;

        clicked => {
            root.checked = !root.checked;
            root.clicked();
        }
    }

    state-layer := StateLayer {
        enabled: root.enabled;
        pressed: touch-area.pressed;
        has-hover: touch-area.has-hover;
        has-focus: touch-area.has-focus;
        background: text_label.color;
    }

    content-layer := HorizontalLayout {
        alignment: center;
        padding-left: 8px;
        padding-right: 8px;
        spacing: 8px;

        text-label := MaterialText {
            vertical-alignment: center;
            color: MaterialPalette.on_surface_variant;
            style: MaterialTypography.label_large;
        }

        icon-image := Image {
            y: (parent.height - self.height) / 2;
            width: MaterialStyleMetrics.size_18;
            colorize: MaterialPalette.on_surface_variant;
            transform-rotation: root.checked ? 180deg : 0;

            animate transform-rotation { duration: 250ms; }
        }
    }

    states [
        disabled when !root.enabled : {
            root.opacity: 0.38;
        }
    ]
}


export component DatePickerPopup inherits PopupWindow {
    in property <string> title;
    in property <Date> date : { day: today[0], month: today[1], year: today[2] };

    callback canceled();
    callback accepted(date: Date);

    // this is used for the navigation between months
    property <string> input_title: @tr("Enter date");
    property <string> input_placeholder_text: "mm/dd/yyyy";
    property <string> input_format: "%m/%d/%Y";
    property <string> cancel_text: @tr("Cancel");
    property <string> ok_text: @tr("Ok");
    property <Date> display_date: root.date;
    property <Date> current_date: root.date;
    property <length> delegate_size: 40px;
    property <length> year_delegate_width: 72px;
    property <int> calendar_column_count: 7;
    property <int> calendar_row_count: 6;
    property <length> calendar_min_width: root.delegate_size * root.calendar_column_count + (root.calendar_column_count - 1) * 1px;
    property <length> calendar_min_height: root.delegate_size *(root.calendar_row_count + 1) + (root.calendar_row_count - 1) * 1px;
    property <int> year_selection_column_count: 3;
    property <int> year_selection_row_count: 5;
    property <bool> year_selection;
    property <bool> selection_mode: true;
    property <string> current_input;

    property <int> calendar_month_count: DatePickerAdapter.month_day_count(root.display_date.month, root.display_date.year);
    property <[string]> calendar_header_model: [
        @tr("One-letter abbrev for Sunday" => "S"),
        @tr("One-letter abbrev for Monday" => "M"),
        @tr("One-letter abbrev for Tuesday" => "T"),
        @tr("One-letter abbrev for Wednesday" => "W"),
        @tr("One-letter abbrev for Thursday" => "T"),
        @tr("One-letter abbrev for Friday" => "F"),
        @tr("One-letter abbrev for Saturday" => "S"),
    ];
    property <[int]> today: DatePickerAdapter.date_now();
    property <int> start_column: DatePickerAdapter.month_offset(root.display_date.month, root.display_date.year);
    property <string> current_month: DatePickerAdapter.format_date("%B %Y", root.display_date.day, root.display_date.month, root.display_date.year);
    property <[int]> year_model: [2024, 2025, 2026, 2027, 2028, 2029, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043];
    property <int> selected_year: root.display_date.year;
    property <int> today_year: root.today[2];
    property <string> current_day: DatePickerAdapter.format_date("%a, %b %d", root.current_date.day, root.current_date.month, root.current_date.year);
    property <[int]> input_formatted: DatePickerAdapter.parse_date(root.current_input, root.input_format);

    close_policy: no_auto_close;
    forward_focus: base;

    base := BaseDialog {
        width: 100%;
        height: 100%;
        title: root.title;
        actions: [
            root.cancel_text,
            root.ok_text
        ];

        content_layer := VerticalLayout {
            spacing: MaterialStyleMetrics.spacing_12;

            header := HorizontalLayout {
                MaterialText {
                    text: root.selection_mode ? root.current_day : root.input_title;
                    horizontal_alignment: left;
                    vertical_alignment: center;
                    style: MaterialTypography.headline_large;
                }

                if root.selection_mode : IconButton {
                    icon: Icons.edit;
                    accessible_label: "Toggle selection mode";

                    clicked => {
                        root.toggle_selection_mode();
                    }
                }
            }

            Rectangle {
                height: 1px;
                background: MaterialPalette.outline;
            }

            if root.selection_mode : HorizontalLayout {
                // FIXME: spacing

                VerticalLayout {
                    horizontal_stretch: 0;
                    alignment: center;

                    SelectionButton {
                        text: root.current_month;
                        icon: Icons.arrow_drop_down;
                        checked <=> root.year_selection;
                    }
                }

                Rectangle {}

                IconButton {
                    icon: Icons.chevron_backward;
                    accessible_label: "Previous month";

                    clicked => {
                        root.show_previous();
                    }
                }

                IconButton {
                    icon: Icons.chevron_forward;
                    accessible_label: "Next month";

                    clicked => {
                        root.show_next();
                    }
                }
            }

            if root.selection_mode : VerticalLayout {
                if !root.year_selection : Calendar {
                    min_width: root.calendar_min_width;
                    min_height: root.calendar_min_height;
                    column_count: root.calendar_column_count;
                    row_count: root.calendar_row_count;
                    delegate_size: root.delegate_size;
                    header_model: root.calendar_header_model;
                    month_count: root.calendar_month_count;
                    today: { day: root.today[0], month: root.today[1], year: root.today[2] };
                    selected_date <=> root.current_date;
                    start_column: root.start_column;
                    display_month: root.display_date.month;
                    display_year: root.display_date.year;

                    select_date(date) => {
                        root.select_date(date);
                    }
                }

                if root.year_selection : YearSelection {
                    min_width: root.calendar_min_width;
                    min_height: root.calendar_min_height;
                    column_count: root.year_selection_column_count;
                    visible_row_count: root.year_selection_row_count;
                    delegate_width: root.year_delegate_width;
                    delegate_height: root.delegate_size;
                    model: root.year_model;
                    selected_year: root.selected_year;
                    today_year: root.today_year;

                    select_year(year) => {
                        root.select_year(year);
                    }
                }
            }

            Rectangle {
                height: 1px;
                visible: root.year_selection;
                background: MaterialPalette.outline;
            }

            if !root.selection_mode : TextField {
                text <=> root.current_input;
                placeholder_text: root.input_placeholder_text;
                trailing_icon: Icons.calendar_today;

                trailing_icon_clicked => {
                    root.toggle_selection_mode();
                }
            }
        }

        action_clicked(index) => {
            // cancel
            if index == 0 {
                root.close();
                root.canceled();
            } else if index == 1 {
            root.accepted(root.date);
            }
        }

        close => {
            root.close();
        }
    }

    changed date => {
        root.display_date = root.date;
        root.current_date = root.date;
    }

    changed selection_mode => {
        // check switch from input mode if input is valid
        if root.selection_mode && root.current_input_valid() {
            root.current_date = root.input_as_date();
            root.display_date = root.current_date;
        }
    }

    pure public function ok_enabled() -> bool {
        root.selection_mode || root.current_input_valid()
    }

    public function get_current_date() -> Date {
        if root.selection_mode {
            return root.current_date;
        }

        root.input_as_date()
    }

    pure function current_input_valid() -> bool {
        DatePickerAdapter.valid_date(root.current_input, root.input_format)
    }

    pure function input_as_date() -> Date {
        { day: root.input_formatted[0], month: root.input_formatted[1], year: root.input_formatted[2] }
    }

    function select_date(date: Date) {
        root.current_date = date;
    }

    function select_year(year: int) {
        root.current_date = { day: 1, month: 1, year: year };
        root.display_date = root.current_date;
        root.year_selection = false;
    }

    function show_next() {
        if root.display_date.month >= 12 {
            root.display_date = { day: 1, month: 1, year: root.display_date.year + 1 };
            return;
        }

        root.display_date = { day: 1, month: root.display_date.month + 1, year: root.display_date.year };
    }

    function show_previous() {
        if root.display_date.month <= 1 {
            root.display_date = { day: 1, month: 12, year: root.display_date.year - 1 };
            return;
        }

        root.display_date = { day: 1, month: root.display_date.month - 1, year: root.display_date.year };
    }

    function toggle_selection_mode() {
        root.selection_mode = !root.selection_mode;
    }
}
